using System;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Diagnostics;
using System.Collections.Generic;

namespace luozhuang.Commons
{
    /// <summary>
    /// 引擎的Socket
    /// </summary>
    public class NSocket
    {
        private object flag = new object();

        /// <summary>
        /// 私有socket
        /// </summary>
        private System.Net.Sockets.Socket socket;
        /// <summary>
        /// 定义了一个可在全局使用的Socket,此Socket可以作为全局属性访问,用于以后给小游戏传递消息
        /// </summary>
        private System.Net.Sockets.Socket Socket
        {
            get { return socket; }
            set { socket = value; }
        }
        /// <summary>
        /// 私有socketArgs_link,它将负责和服务器连接的消息
        /// </summary>
        private SocketAsyncEventArgs socketArgs_link;
        /// <summary>
        /// 真正发送的东西,作为一个发送点,依次发送消息,为了不和服务器连接的冲突,这里单独使用一个
        /// </summary>
        private SocketAsyncEventArgs _sendEventArgs;


        /// <summary>
        /// Socket连接关闭
        /// </summary>
        //public event Action SocketClose;
        public event Action<string> SocketCloseIP;

        /// <summary>
        /// 最后一次发送消息的时间
        /// </summary>
        public DateTime lastSendMessageTime = DateTime.Now;
        /// <summary>
        /// 构造函数
        /// </summary>
        public NSocket()
        {

        }
        private string serverIP;
        private string serverPort;
        /// <summary>
        /// 游戏连接服务器
        /// </summary>
        /// <param name="serverIP"></param>
        /// <param name="serverPort"></param>
        public void GameConnectServer(string serverIP, string serverPort)
        {
            this.serverIP = serverIP;
            this.serverPort = serverPort;
            GameSocketState = "启动连接服务器";
            GameSocketConnect(serverIP, serverPort);
        }
        private void GameSocketConnect(string serverIP, string serverPort)
        {
            socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socketArgs_link = new SocketAsyncEventArgs();
            socketArgs_link.RemoteEndPoint = new DnsEndPoint(serverIP, Convert.ToInt32(serverPort));
            //设置好当Socket任何一个动作完成时的回调函数。
            socketArgs_link.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
            //执行连接。
            socket.ConnectAsync(socketArgs_link);
        }
        /// <summary>
        /// 关闭链接但是不销毁注册消息
        /// </summary>
        public void GameSocketCloseNoEvent()
        {
            GameSocketState = "使用了断开";
            if (socket != null)
            {
                if (_sendEventArgs != null)
                    _sendEventArgs.Dispose();
                _sendEventArgs = null;
                lock (flag) //锁住下面的代码,让同一时间内只能一个线程访问
                {
                    if (socketArgs_link != null)
                    {
                        socketArgs_link.Completed -= OnSocketConnectCompleted;
                        socketArgs_link.Completed -= OnSocketReceiveCompleted;
                        try
                        {
                            socketArgs_link.Dispose();
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.ToString());
                        }
                    }
                }
                socketArgs_link = null;
            } 
        }
        /// <summary>
        /// 当游戏关闭的时候会调用这里关掉socket
        /// 或者在外部进行断开操作
        /// </summary>
        public void GameSocketClose()
        {
            //if (SocketClose != null)
            //    SocketClose();
            if (SocketCloseIP != null)
                SocketCloseIP(serverIP);
            GameSocketState = "使用了断开";            
            if (socket != null)
            {                
                if (_sendEventArgs != null)
                    _sendEventArgs.Dispose();
                _sendEventArgs = null;
                lock (flag) //锁住下面的代码,让同一时间内只能一个线程访问
                {
                    if (socketArgs_link != null)
                    {
                        socketArgs_link.Completed -= OnSocketConnectCompleted;
                        socketArgs_link.Completed -= OnSocketReceiveCompleted;
                        try
                        {
                            socketArgs_link.Dispose();
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.ToString());
                        }
                    }
                }
                socketArgs_link = null;
            }            
        }
        /// <summary>
        /// 获得是否连接状态
        /// </summary>
        public bool Connected
        {
            get 
            {
                if (socket == null)
                    return false;
                else
                    return socket.Connected; 
            }
        }
        /// <summary>
        /// 连接成功的委托
        /// </summary>
        /// <param name="sender"></param>
        public delegate void SocketConnected(NSocket sender);
        /// <summary>
        /// 连接成功事件
        /// </summary>
        public event SocketConnected OnSocketConnected;
        /// <summary>
        /// 连接成功时调用的虚函数
        /// </summary>
        protected virtual void GameSocketConnected()
        {
            memStream = new MemoryStream();
            reader = new BinaryReader(memStream);
            if (OnSocketConnected != null)
                OnSocketConnected(this);
        }
        /// <summary>
        /// 网络断开函数,当网络出现断开的时候Game调用此函数
        /// 可以通过重写该函数获得断开的内容
        /// </summary>
        public void GameSocketClosed()
        {
            GameSocketClose();
            GameSocketState = "Socket网络断开";
        }
        /// <summary>
        /// 
        /// </summary>
        public void GameSocketClosedNoEvent()
        {
            GameSocketCloseNoEvent();
            GameSocketState = "Socket网络断开";
        }
        /// <summary>
        /// Socket状态,可以获得一些信息,比如是否断开连接
        /// </summary>
        public string GameSocketState = "默认内容";
        /// <summary>
        /// 这是当Socket连接成功之后的事件,它将重新分配接收
        /// SocketArgs_Link在这里转为接收信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">SocketArgs_Link它本身</param>
        private void OnSocketConnectCompleted(object sender, SocketAsyncEventArgs e)
        {
            System.Net.Sockets.Socket thisSocket = sender as System.Net.Sockets.Socket;
            if (!thisSocket.Connected)
            {
                GameSocketState = "无法连接服务器";
                //GameSocketClosed();
                return;
            }
            else
            {
                GameSocketConnected();
                GameSocketState = "成功连接服务器";

                // 构造一个 SocketAsyncEventArgs 对象,用于用户向服务端发送消息
                _sendEventArgs = new SocketAsyncEventArgs();
                //_sendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(_sendEventArgs_Completed);
                _sendEventArgs.RemoteEndPoint = e.RemoteEndPoint;
                // 设置数据缓冲区
                byte[] response = new byte[81920];
                e.SetBuffer(response, 0, response.Length);
                e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);
                e.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketReceiveCompleted);
                // 继续异步地从服务端 Socket 接收数据
                try
                {
                    thisSocket.ReceiveAsync(socketArgs_link);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message.ToString());
                }
            }
        }
        /// <summary>
        /// 这是当Socket收到消息之后的事件,解析e可以获得详细信息
        /// 这里将定义大量的消息代码
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">e.Buffer</param>
        private void OnSocketReceiveCompleted(object sender, SocketAsyncEventArgs e)
        {
            GameSocketState = "正在接收消息";
            if (e.BytesTransferred == 0)//说明连接已经断开
            {
                e.Completed -= new EventHandler<SocketAsyncEventArgs>(OnSocketReceiveCompleted);

                GameSocketClosed();
                return;
            }
            Byte[] data = new Byte[e.BytesTransferred];
            Array.Copy(e.Buffer, e.Offset, data, 0, data.Length);
            Receive(data);
            socket.ReceiveAsync(e);
        }

        private MemoryStream memStream;
        private BinaryReader reader;
        private void Receive(byte[] bytes)
        {
            memStream.Seek(0, SeekOrigin.End);
            memStream.Write(bytes, 0, bytes.Length);
            //Reset to beginning
            memStream.Seek(0, SeekOrigin.Begin);
            while (RemainingBytes() > 4)
            {
                ushort messageLen = (ushort)(Converter.ReadInt(reader) - 4);
                if (RemainingBytes() >= messageLen)
                {
                    MemoryStream messageStream = new MemoryStream();
                    BinaryWriter messageWriter = new BinaryWriter(messageStream);
                    messageWriter.Write(reader.ReadBytes(messageLen));
                    messageStream.Seek(0, SeekOrigin.Begin);
                    BinaryReader br = new BinaryReader(messageStream);
                    ushort type = Converter.ReadShort(br);

                    MessageReceive(type, br.ReadBytes(messageLen - 2));

                    messageWriter.Close();
                    messageStream.Close();
                }
                else
                {                    
                    memStream.Position = memStream.Position - 4;
                    break;
                }
            }
            
            byte[] leftover = reader.ReadBytes((int)RemainingBytes());
            
            memStream.SetLength(0);
            memStream.Write(leftover, 0, leftover.Length);
        }

        private long RemainingBytes()
        {
            return memStream.Length - memStream.Position;
        }

        /// <summary>
        /// 消息到来的委托
        /// </summary>
        /// <param name="messageID"></param>
        /// <param name="message"></param>
        public delegate void MessageReceived(ushort messageID, byte[] message);
        /// <summary>
        /// 消息到来的事件
        /// </summary>
        public event MessageReceived OnMessageReceive;
        /// <summary>
        /// 收到消息,重写该函数可以得到想要的消息号和消息内容
        /// </summary>
        /// <param name="messageID"></param>
        /// <param name="message"></param>
        public virtual void MessageReceive(ushort messageID, byte[] message)
        {
            if (OnMessageReceive != null)
            {
                OnMessageReceive(messageID, message);
            }
        }
        /// <summary>
        /// 发送一段消息到服务器
        /// </summary>
        /// <param name="messID"></param>
        /// <param name="details"></param>

        public virtual void SendMessageToServer(ushort messID, object details)
        {
            GameSocketState = "正在发送消息";
            if (!socket.Connected)
            {
                GameSocketState = "无法连接服务器";
                GameSocketClosed();
                return;
            }
            MemoryStream ms = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(ms);
            MemoryStream ms_header = new MemoryStream();
            BinaryWriter writer_header = new BinaryWriter(ms_header);
            byte[] bytes = null;
            //无论怎么样都要写一个消息头
            Converter.WriteShort(writer, (ushort)messID);
            if (details != null)
                writer.Write((byte[])details);
            Converter.WriteInt(writer_header, (int)(ms.Length) + sizeof(int));
            bytes = new byte[ms_header.Length + ms.Length];
            var ms_header_array = ms_header.ToArray();
            ms_header_array.CopyTo(bytes, 0);
            ms.ToArray().CopyTo(bytes, ms_header_array.Length);
            _sendEventArgs = new SocketAsyncEventArgs();
            //_sendEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(_sendEventArgs_Completed);
            _sendEventArgs.UserToken = bytes;
            _sendEventArgs.SetBuffer(bytes, 0, bytes.Length);
            socket.SendAsync(_sendEventArgs);
            writer.Close();
            ms.Close();
            writer_header.Close();
            ms_header.Close();
            lastSendMessageTime = DateTime.Now;
            AddLastTime(messID);
            
        }
        /// <summary>
        /// 发送消息时判断一下是否是在短时间内连续发送相同消息,参数isJudgeBatter指示是否进行这种判断,TimeInterval为时间间隔(毫秒)
        /// </summary>
        private ushort preMessID;
        private Dictionary<ushort, DateTime> lastSendMessageTimeList = new Dictionary<ushort, DateTime>();
        /// <summary>
        /// 最后时间判定
        /// </summary>
        /// <param name="messID"></param>
        public void AddLastTime(ushort messID)
        {
            if (lastSendMessageTimeList.ContainsKey(messID))
            {
                lastSendMessageTimeList[messID] = lastSendMessageTime;
            }
            else
            {
                lastSendMessageTimeList.Add(messID, lastSendMessageTime);
            }
        }
        /// <summary>
        /// 发送消息时判断一下是否是在短时间内连续发送相同消息,参数isJudgeBatter指示是否进行这种判断,TimeInterval为时间间隔(毫秒)
        /// </summary>
        /// <param name="messID"></param>
        /// <param name="details"></param>
        /// <param name="isJudgeBatter"></param>
        /// <param name="TimeInterval"></param>
        public virtual void SendMessageToServer(ushort messID, object details, bool isJudgeBatter, double TimeInterval)
        {
            if (isJudgeBatter == true)
            {
                if (messID == preMessID)
                {
                    if (lastSendMessageTimeList.ContainsKey(messID))
                    {
                        if ((DateTime.Now - lastSendMessageTimeList[messID]).TotalMilliseconds < TimeInterval)
                        {
                            preMessID = messID;
                            lastSendMessageTime = DateTime.Now;
                            AddLastTime(messID);
                            return;
                        }
                    }
                }
            }
            GameSocketState = "正在发送消息";
            if (!socket.Connected)
            {
                GameSocketState = "无法连接服务器";
                GameSocketClosed();
                return;
            }
            MemoryStream ms = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(ms);
            MemoryStream ms_header = new MemoryStream();
            BinaryWriter writer_header = new BinaryWriter(ms_header);
            byte[] bytes = null;
            //无论怎么样都要写一个消息头
            Converter.WriteShort(writer, (ushort)messID);
            if (details != null)
                writer.Write((byte[])details);
            Converter.WriteInt(writer_header, (int)(ms.Length) + sizeof(int));
            bytes = new byte[ms_header.Length + ms.Length];
            ms_header.ToArray().CopyTo(bytes, 0);
            ms.ToArray().CopyTo(bytes, ms_header.ToArray().Length);
            _sendEventArgs = new SocketAsyncEventArgs();
            _sendEventArgs.UserToken = bytes;
            _sendEventArgs.SetBuffer(bytes, 0, bytes.Length);
            socket.SendAsync(_sendEventArgs);
            writer.Close();
            ms.Close();
            writer_header.Close();
            ms_header.Close();

            preMessID = messID;
            lastSendMessageTime = DateTime.Now;
            AddLastTime(messID);

        }
    }
}
